iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
Vibe Coding

使用 Vibe Coding 開發一套 Tutting 模擬器吧!系列 第 13

📐DAY 13 | 兩節解析解 IK(Two-Bone Analytic IK) 與雙球控制系統實作

  • 分享至 

  • xImage
  •  

昨天 DAY 13 探討了 Tutting 的四項中階技術(Isolation、Fixed Point、Tracing、Threading),當我照鏡子練舞的同時,突然開始思考:要如何在 3D Tutting 模擬器中實踐這四項充滿空間定格感的幾何招式呢?

在與 AI 夥伴深入討論與腦力激盪後,我們得出了一個關鍵結論:要讓 XBOT 擺脫死板的機械感,我們不能只靠單一骨骼一根一根旋轉(FK),而是需要「點選目標,自動解算關節角度」的 IK(逆向運動學)功能!

今天 DAY 14,我們要正式把 手腳兩節解析解 IK(Two-Bone Analytic IK)系統 與 橙/黃綠雙球控制面板 寫進我們的 3D 模擬器裡!

https://ithelp.ithome.com.tw/upload/images/20260907/20144288u4nZsan9O9.jpg

什麼是兩節解析解 IK(Two-Bone Analytic IK)

「兩節解析解 IK」(Two-Bone Analytic IK)是動畫/遊戲程式裡最常見的一種反向運動學(Inverse Kinematics, IK)解法,專門用來解決「兩段式肢體」的姿勢問題,例如手臂(肩→肘→手)或腿(髖→膝→腳)。

兩節解析解 IK 的計算流程如下:

https://ithelp.ithome.com.tw/upload/images/20260828/20144288dD2y1J7Ekb.png

核心概念:目標球 vs. 極向球(Pole Target)

單靠 FK 擺姿勢的缺點極其明顯:想調整手腕貼住臉部,需要先轉肩膀、再轉手肘、最後轉手腕,微調一個姿勢就要花上好幾分鐘。

現在我們引入了雙球控制架構,四條肢體(右手 / 左手 / 右腳 / 左腳)皆可獨立開啟 IK:

  • 🟠 末端目標球(橘色實心球):決定手掌(Hand)或腳掌(Foot)要抵達的世界空間座標。拖曳它,整條手臂或腿部就會自然伸展。
  • 🟢 彎曲極向球 Pole Target(黃綠色八面體):控制手肘(ForeArm)或膝蓋(Leg)要往哪個方向彎曲。它不影響末端位置,專門解決手肘彎錯方向的平面角度問題。

https://ithelp.ithome.com.tw/upload/images/20260823/20144288WCNQ52lAMS.png

結語

擁有了 Two-Bone Analytic IK 系統後,我們要設定一個 Fixed Point(空間不動點)或複雜的手臂幾何姿態,只需要把橙色目標球往目標位置一拖、再調校黃綠極向球控制手肘面向,就能在短時間內精準完成編舞!


Tutting 3D 模擬器加入手腳 IK 功能核心實作 Prompt

# Prompt:為 Tutting 3D 模擬器加入手腳 IK 功能

## 背景
現有一個純瀏覽器單頁 3D 人體姿勢關鍵影格動畫編輯器(three.js r160,Xbot.glb / Mixamo 骨架,
`mixamorig` 命名慣例,四肢骨骼鍵名為 `rArm/rForeArm/rHand`、`lArm/lForeArm/lHand`、
`rUpLeg/rLeg/rFoot`、`lUpLeg/lLeg/lFoot`)。目前所有關節都是 FK:每個骨骼有獨立的可點擊
關節球(cyan sphere),點選後用 `TransformControls`(rotate 模式)直接轉動骨骼,角度存在
`target[key]`/`current[key]` 這兩個 Euler 角度陣列裡,每幀用 spring lerp 趨近並乘上
`restQuat[key]` 套用到骨骼上。

請幫我在這個系統上,**新增手腳的 IK 功能**,讓四肢也可以用「拖曳末端球」的方式擺姿勢,
而不是只能一根一根骨骼轉。

## 核心概念:兩顆控制球

1. **末端目標球(橙色實心球)**:拖曳它決定手掌或腳掌想要抵達的世界座標。
2. **彎曲極向球 Pole Target(黃綠色八面體)**:專門控制手肘或膝蓋要往哪個方向彎曲
   (彎曲平面法向量的參考點,不影響末端位置,只影響中間關節彎向哪一側)。

四條肢體(右手/左手/右腳/左腳)各自獨立一組「目標球+極向球」,可分別開關 IK。

## 功能需求細節

### 1. IK 鏈與骨骼對應
- 手:`root=rArm/lArm`、`mid=rForeArm/lForeArm`、`end=rHand/lHand`
- 腳:`root=rUpLeg/lUpLeg`、`mid=rLeg/lLeg`、`end=rFoot/lFoot`
- 用一個設定物件(例如 `IK_CHAINS`)集中管理這四條鏈的 root/mid/end 鍵名、
  顯示用中文標籤、以及極向球的預設偏移量。

### 2. IK 求解演算法:兩節解析解(Two-Bone IK)
- 給定 root/mid/end 三根骨骼目前的世界座標,算出 `upperLen`(root→mid 長度)與
  `lowerLen`(mid→end 長度)。
- 把目標點與 root 的距離 `dist` 夾在 `[|upperLen-lowerLen|, upperLen+lowerLen]` 之間
  (避免無解/超出可及範圍時骨骼被拉爆)。
- 用**餘弦定理**算出 root 端夾角:
  `cosRoot = (upperLen² + dist² - lowerLen²) / (2·upperLen·dist)`,`angleRoot = acos(cosRoot)`。
- **彎曲方向**由極向球決定:取 `root→pole` 向量在「垂直於 root→target 方向」上的分量,
  當作彎曲方向 `bendDir`;`bendAxis = normalize(cross(targetDir, bendDir))` 就是彎曲的旋轉軸。
- 把 `targetDir` 繞 `bendAxis` 轉 `angleRoot` 度,得到新的 `mid` 方向 `midDir`;
  新的 `end` 位置就是夾好距離後的 `clampedTargetPos`(餘弦定理保證這個點與新 mid 位置
  恰好相距 `lowerLen`,不需要再另外算一次)。
- **套用到骨骼**:因為 `bone.quaternion` 是相對 parent 的本地旋轉,要先算出「世界空間的
  旋轉增量」,再換算回本地:
  ``javascript
  newWorldQuat = deltaQuat * boneWorldQuat
  newLocalQuat = inverse(parentWorldQuat) * newWorldQuat
  ``
  先對 root 骨骼套用並呼叫 `bone.updateWorldMatrix(true, true)` 讓 mid/end 的世界矩陣
  立刻反映最新旋轉,再重新取得 mid/end 的世界座標,用同樣方法算出 mid 骨骼要轉的量並套用。

### 3. 與既有 FK / 拍點系統的相容性(重要)
- 每個肢體有獨立開關 `ikEnabled[limb]`。開啟時:
  - 該肢體 root/mid 骨骼的關節球**暫時隱藏**,避免跟 IK 互搶(FK 的逐幀 lerp 迴圈要跳過
    這兩個 key)。
  - 末端骨骼自己的旋轉(手腕/腳踝)**仍保留 FK 可調**,不受 IK 影響。
- 每幀 IK 求解完後,把結果**反推回** `target[key]`/`current[key]`(用跟現有「從骨骼四元數
  反推相對 rest pose 角度」相同的邏輯:`deltaQuat = restQuat⁻¹ · bone.quaternion` →
  轉 Euler → 存回陣列)。這樣 IK 擺好的姿勢才能直接「新增拍點」、或匯出到既有的 JSON 面板,
  完全相容原本的 keyframe/Slerp 播放系統(播放時不需要即時重新求解 IK,直接用烘焙好的
  FK 角度做四元數 Slerp 即可)。
- 開啟 IK 當下,要把目標球/極向球的初始位置對齊「目前姿勢」算出的預設值(末端骨骼目前世界
  座標、mid 骨骼世界座標 + 一個預設偏移量),避免一開啟就瞬間跳動。

### 4. 選取與互動
- 場景裡除了原本的關節球,還要能點擊選到目標球/極向球。用統一的挑選函式收集「目前可見」
  的所有可點物件(關節球 + 該肢體 IK 開啟時才可見的目標球/極向球),依 `userData` 上標記的
  類型分派到「選關節」或「選 IK 球」。
- 目標球/極向球用**另一個獨立的 `TransformControls`**,固定 `translate` 模式(旋轉環那個
  維持給關節用),避免兩種操作模式互相干擾。
- 選取狀態需要能顯示「目前選取哪顆球、它的世界座標」,並提供一個「重置此球位置」的按鈕
  (對應原本關節選取時的「此關節歸零」按鈕,同一顆按鈕依選取類型切換行為/文字)。
- Esc、點空白處取消選取、`suppressClick` 防誤觸的邏輯要同時涵蓋兩種 TransformControls。

### 5. UI
- 在既有分頁列(姿勢庫/JSON/時間軸)旁新增一個「手腳 IK」分頁,內容包含:
  - 4 個開關按鈕:右手 IK/左手 IK/右腳 IK/左腳 IK(比照現有 `.active` 樣式高亮)
  - 簡短說明文字:橘色球=目標、黃綠色八面體=極向球、彎錯邊可直接拖到另一側修正
- 保持跟現有視覺風格一致(深色卡片、cyan/pink 主題色,這裡目標球用橘色、極向球用
  黃綠色以便跟原本 cyan 關節球區分)。

## 已知限制(可以先不做,但要讓使用者知道)
- 極向球的預設偏移方向是「合理猜測」(假設角色面向 +Z),沒辦法在開發環境實際渲染驗證,
  彎錯邊時使用者手動拖曳極向球到另一側即可修正。
- 不需要做「腳掌貼地鎖旋轉」(IK 過程中鎖住 ankle 世界旋轉不隨腿彎動而跑掉)這種進階行為,
  單純兩節 IK 即可,之後有需要再加。
- 純滑鼠 IK,不需要碰撞偵測或關節角度限制(clamp joint limits)。


上一篇
📐DAY 12 | 探討 Tutting 舞蹈四項中階技術
下一篇
📐DAY 14 | 修正 FK 切換 IK 姿勢跳動 Bug 🐛
系列文
使用 Vibe Coding 開發一套 Tutting 模擬器吧!25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言